﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading;

namespace JustCleanIt.Common
{
    /// <summary>
    /// Log Manager to distribute the logs depending upon the configuration.
    /// </summary>
    public class LogManager
    {
        #region Constants
        public const string LOG_TIMESTAMP_FORMAT = "yyyy-MM-dd HH:mm:ss:fff";
        #endregion

        #region Static Fields
        private static IDictionary<Type, LogManager> myLoggerMap = new Dictionary<Type, LogManager>();
        private static TraceSource mySource = new TraceSource("LogSource");
        #endregion

        #region Private Members
        private string myOriginatorTypeName;
        #endregion

        #region Properties
        /// <summary>
        /// Gets whether object is disposed.
        /// </summary>
        public bool IsDisposed { get; private set; }
        #endregion

        #region Constructors
        /// <summary>
        /// Construct LogManager instance.
        /// </summary>
        static LogManager()
        {
            try
            {
                // TODO: initialize required things
            }
            catch { }
        }

        /// <summary>
        /// Construct LogManager instance.
        /// </summary>
        /// <param name="originatorType">Type of the object.</param>
        private LogManager(Type originatorType)
        {
            this.myOriginatorTypeName = originatorType.Name;
        }
        #endregion

        #region Private Methods
        /// <summary>
        /// Post messages to Log File.
        /// </summary>
        /// <param name="message">Message to post</param>
        /// <param name="eventType">Type of the Event</param>        
        private static void Log(string originator, string message, EventType eventType, ushort eventId)
        {
            try
            {
                TraceEventType traceEventType = GetTraceEventType(eventType);
                message = string.Format("{0} [{1,5}] - {2}-{3}{4}",
                                DateTime.Now.ToString(LOG_TIMESTAMP_FORMAT),
                                Thread.CurrentThread.ManagedThreadId, originator, message, Environment.NewLine);

                mySource.TraceEvent(traceEventType, eventId, message);
            }
            catch
            {
                message = "Error while writing message to Log File.";
                System.Diagnostics.Trace.WriteLine(message);
            }
        }

        /// <summary>
        /// Get TraceSource EventType
        /// </summary>
        /// <param name="eventType"></param>
        /// <returns></returns>
        private static TraceEventType GetTraceEventType(EventType eventType)
        {
            TraceEventType traceEventType = TraceEventType.Information;
            switch (eventType)
            {
                case EventType.Information:
                    traceEventType = TraceEventType.Information;
                    break;
                case EventType.Exception:
                    traceEventType = TraceEventType.Error;
                    break;
                case EventType.Warning:
                    traceEventType = TraceEventType.Warning;
                    break;
                case EventType.Fatal:
                    traceEventType = TraceEventType.Critical;
                    break;
            }
            return traceEventType;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        /// <param name="isDisposing">true to dispose managed resources.</param>
        protected virtual void Dispose(bool isDisposing)
        {
            try
            {
                if (!IsDisposed)
                {
                    //Clean up unmanaged if any

                    //Clean up managed
                    if (isDisposing)
                    {

                    }
                    IsDisposed = true;
                }
            }
            finally
            {
                //base.Dispose(disposing);
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Get Log Manager instance.
        /// </summary>
        /// <param name="type">Object Type</param>
        /// <returns>LogManager instance for given Type.</returns>
        public static LogManager GetLogger(Type type)
        {
            lock (myLoggerMap)
            {
                if (!myLoggerMap.ContainsKey(type))
                {
                    myLoggerMap.Add(type, new LogManager(type));
                }

                return myLoggerMap[type];
            }
        }

        /// <summary>
        /// Post messages to Log File as Information.
        /// </summary>
        /// <param name="message">Message to post</param>
        public void LogInfo(string message, ushort eventId = EventIds.GeneralInformation)
        {
            Log(myOriginatorTypeName, message, EventType.Information, eventId);
        }

        /// <summary>
        /// Post messages to Log File as Information.
        /// </summary>
        /// <param name="message">Message to post</param>
        /// <param name="args">zero or more arguments to format the message</param>
        public void LogInfoFormat(string message, ushort eventId = EventIds.GeneralInformation, params object[] args)
        {
            Log(myOriginatorTypeName, string.Format(message, args), EventType.Information, eventId);
        }

        /// <summary>
        /// Post messages to Log File as Warning.
        /// </summary>
        /// <param name="message">Message to post</param>
        public void LogWarning(string message, ushort eventId = EventIds.GeneralWarning)
        {
            Log(myOriginatorTypeName, message, EventType.Warning, eventId);
        }

        /// <summary>
        /// Post messages to Log File as Warning.
        /// </summary>
        /// <param name="message">Message to post</param>
        /// <param name="args">zero or more arguments to format the message</param>
        public void LogWarningFormat(string message, ushort eventId = EventIds.GeneralWarning, params object[] args)
        {
            Log(myOriginatorTypeName, string.Format(message, args), EventType.Warning, eventId);
        }

        /// <summary>
        /// Post messages to Log File as Exception.
        /// </summary>        
        /// <param name="ex">Exception to be logged</param>
        public void LogException(Exception ex)
        {
            LogException(string.Empty, ex);
        }

        /// <summary>
        /// Post messages to Log File as Exception.
        /// </summary>
        /// <param name="message">Message to log</param>
        /// <param name="ex">Exception to be logged</param>
        public void LogException(string message, Exception ex, ushort eventId = EventIds.GeneralException)
        {
            Log(myOriginatorTypeName, string.Format("{0}\nException:{1}\nStackTrace:{2}",
                (message != null ? message : string.Empty), ex.Message, ex.StackTrace), EventType.Exception, eventId);
        }

        /// <summary>
        /// Post messages to Log File as Exception.
        /// </summary>
        /// <param name="message">Message to log</param>        
        public void LogFatalError(Exception ex)
        {
            LogFatalError(string.Empty, ex);
        }

        /// <summary>
        /// Post messages to Log File as Exception.
        /// </summary>
        /// <param name="message">Message to log</param>
        /// <param name="ex">Exception to be logged</param>
        public void LogFatalError(string message, Exception ex, ushort eventId = EventIds.GeneralFatal)
        {
            Log(myOriginatorTypeName, string.Format("{0}\nException:{1}\nStackTrace:{2}",
                (message != null ? message : string.Empty), ex.Message, ex.StackTrace), EventType.Fatal, eventId);
        }

        /// <summary>
        /// Post messages to Log File as Exception.
        /// </summary>        
        /// <param name="ex">Exception to be logged</param>
        public void Log(string message, EventType eventType, ushort eventId)
        {
            Log(myOriginatorTypeName, message, eventType, eventId);
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this); //Don't trigger finalizer
        }

        #endregion
    }
}
